#include "stdafx.h"
#include "TString.h"
#include "malloc.h"

#ifdef __GNUG__
typedef long long int __int64;
#endif

TString::TString(int nSize)
{
	m_nMaxSize = nSize;
	m_nSize =0;
	if( nSize>0 ) 
	m_pData = new char[nSize];
	 else m_pData = NULL;
}

TString::~TString()
{
   if( m_pData ) delete m_pData;
}

int TString::Remove(int length)
{
	if( length >= m_nSize ){
		m_nSize=0;
		return 0;
	}
	memmove(m_pData,m_pData+length,m_nSize-length);
	m_nSize -= length;
	return m_nSize;
}

void TString::SetSize(int nNewSize)
{
	if (nNewSize == 0)
	{
		// shrink to nothing
		if (m_pData != NULL)
		{
			delete[] m_pData;
			m_pData = NULL;
		}
		m_nSize = m_nMaxSize = 0;
		return;
	}
	else if (m_pData == NULL)
	{
		// create one with exact size
		m_pData = new char[nNewSize];
		memset(m_pData,0,nNewSize);
		m_nSize = 0;
		m_nMaxSize = nNewSize;
	}
	
	if (nNewSize <= m_nMaxSize)
	{
		// it fits
		if (nNewSize > m_nSize)
		{
			// initialize the new elements
			memset(&m_pData[m_nSize],0, (nNewSize-m_nSize));
		}
		else if (m_nSize > nNewSize)
		{
			// destroy the old elements
			memset(&m_pData[nNewSize],0, (m_nSize-nNewSize));
		}
	}
	else
	{
		// heuristically determine growth when nGrowBy == 0
		//  (this avoids heap fragmentation in many situations)
		int nGrowBy = m_nSize / 8;
		nGrowBy = (nGrowBy < 4) ? 4 : ((nGrowBy > 1024) ? 1024 : nGrowBy);
		
		int nNewMax;
		if (nNewSize < m_nMaxSize + nGrowBy)
			nNewMax = m_nMaxSize + nGrowBy;  // granularity
		else
			nNewMax = nNewSize;  // no slush

		char* pNewData = new char[nNewMax ];

		// copy new data from old
		memcpy(pNewData, m_pData, m_nSize );

		// construct remaining elements
	//	ASSERT(nNewSize > m_nSize);
		memset(&pNewData[m_nSize], 0,(nNewSize-m_nSize));

		// get rid of old stuff (note: no destructors called)
		delete[] m_pData;
		m_pData = pNewData;
		m_nMaxSize = nNewMax;
	}
}

void TString::MoveTo(TString &packet)
{
	if(packet.m_pData)
	{
		delete packet.m_pData;
	}
	packet.m_pData = m_pData;
	packet.m_nMaxSize = m_nMaxSize;
	packet.m_nSize = m_nSize;
	m_pData = NULL;
	m_nSize = 0;
	m_nMaxSize = 0;

}
void TString::Trunc()
{
	m_nSize =0;
	if( m_nMaxSize > 2048 ) SetSize(0);
}
int TString::Add(const void * pValue,int length)
{
	if( (m_nSize+length)>=m_nMaxSize ) SetSize(m_nSize+length);
	memcpy(m_pData+m_nSize,pValue,length);
	m_nSize += length;
	return m_nSize;
}

int TString::Set(int offset,const void * pValue,int length)
{
	if( (offset+length)>=m_nMaxSize ) SetSize(offset+length);
	memcpy(m_pData+offset,pValue,length);
	if( (offset+length)>m_nSize ) m_nSize = offset+length;
	return 1;
}

int TString::Insert(int offset,const void *pValue, int length)
{
	if( (m_nSize+length)>=m_nMaxSize ) SetSize(m_nSize+length);
	memmove(m_pData+offset+length,m_pData+offset,m_nSize-offset);
	memmove(m_pData+offset,pValue,length);
	m_nSize += length;
	return 1;
}
char * TString::GetBuffer(int nMinBufLength)
{
	ASSERT(nMinBufLength >= 0);
	
	SetSize(nMinBufLength+1);
	// return a pointer to the character storage for this string
	ASSERT(m_pData != NULL);
	return m_pData;
}
void TString::ReleaseBuffer(int nNewLength)
{
	if (nNewLength == -1)
		nNewLength = (int)strlen(m_pData); // zero terminated

	ASSERT(nNewLength <= m_nMaxSize);
	m_nSize = nNewLength;
	m_pData[nNewLength] = '\0';
}
void TString::Append(TCHAR ch)
{
	if ( m_nSize + 2 > m_nMaxSize ) SetSize(m_nSize + 4);
	m_pData[m_nSize++]=ch;
	m_pData[m_nSize] = '\0';
	return ;
}
void TString::Append( LPCTSTR lpszSrcData)
{
	//  -- the main routine for += operators
	if( lpszSrcData == NULL ) return ;
	// concatenating an empty string is a no-op!
	int nSrcLen = strlen(lpszSrcData)+1;

	if( nSrcLen==1 ) return;

	// if the buffer is too small, or we have a width mis-match, just
	//   allocate a new buffer (slow but sure)
	if ( m_nSize + nSrcLen > m_nMaxSize ) SetSize(m_nSize + nSrcLen);

		// fast concatenation when buffer big enough
		memcpy(m_pData+m_nSize, lpszSrcData, nSrcLen  );
		nSrcLen--;
		m_nSize += nSrcLen;

}
int TString::Find(TCHAR ch) const
{
	return Find(ch, 0);
}

int TString::Find(TCHAR ch, int nStart) const
{
	int nLength = m_nSize;
	if (nStart >= nLength)
		return -1;

	// find first single character
	LPTSTR lpsz = strchr(m_pData + nStart, ch);

	// return -1 if not found and index otherwise
	return (lpsz == NULL) ? -1 : (int)(lpsz - m_pData);
}
int TString::ReverseFind(TCHAR ch) const
{
	// find last single character
	LPTSTR lpsz = strrchr(m_pData, ch);

	// return -1 if not found, distance from beginning otherwise
	return (lpsz == NULL) ? -1 : (int)(lpsz - m_pData);
}

// find a sub-string (like strstr)
int TString::Find(LPCTSTR lpszSub) const
{
	return Find(lpszSub, 0);
}

int TString::Find(LPCTSTR lpszSub, int nStart) const
{
//	ASSERT(AfxIsValidString(lpszSub));

	int nLength = m_nSize;
	if (nStart > nLength)
		return -1;

	// find first matching substring
	LPTSTR lpsz = strstr(m_pData + nStart, lpszSub);

	// return -1 for not found, distance from beginning otherwise
	return (lpsz == NULL) ? -1 : (int)(lpsz - m_pData);
}

#define SHRT_MIN    (-32768)        /* minimum (signed) short value */
#define SHRT_MAX      32767         /* maximum (signed) short value */
#define USHRT_MAX     0xffff        /* maximum unsigned short value */
#define INT_MIN     (-2147483647 - 1) /* minimum (signed) int value */
#define INT_MAX       2147483647    /* maximum (signed) int value */
#define UINT_MAX      0xffffffff    /* maximum unsigned int value */
#define LONG_MIN    (-2147483647L - 1) /* minimum (signed) long value */
#define LONG_MAX      2147483647L   /* maximum (signed) long value */
#define ULONG_MAX     0xffffffffUL  /* maximum unsigned long value */

#define TCHAR_ARG   int
#define WCHAR_ARG   int
#define CHAR_ARG    int
#define LPWSTR char *

#ifdef _X86_
	#define DOUBLE_ARG  _AFX_DOUBLE
#else
	#define DOUBLE_ARG  double
#endif

#define FORCE_ANSI      0x10000
#define FORCE_UNICODE   0x20000
#define FORCE_INT64     0x40000

static int vscprintf(LPCTSTR lpszFormat,va_list argList)
{
	// make a guess at the maximum length of the resulting string
	int nMaxLen = 0;
	for (LPCTSTR lpsz = lpszFormat; *lpsz != '\0'; lpsz++)
	{
		// handle '%' character, but watch out for '%%'
		if (*lpsz != '%' || * (lpsz++) == '%')
		{
			nMaxLen += strlen(lpsz);
			continue;
		}

		int nItemLen = 0;

		// handle '%' character with format
		int nWidth = 0;
		for (; *lpsz != '\0'; lpsz++)
		{
			// check for valid flags
			if (*lpsz == '#')
				nMaxLen += 2;   // for '0x'
			else if (*lpsz == '*')
				nWidth = va_arg(argList, int);
			else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' ||
				*lpsz == ' ')
				;
			else // hit non-flag character
				break;
		}
		// get width and skip it
		if (nWidth == 0)
		{
			// width indicated by
			nWidth = atoi(lpsz);
			for (; *lpsz != '\0' && isdigit(*lpsz); lpsz++)
				;
		}
		ASSERT(nWidth >= 0);

		int nPrecision = 0;
		if (*lpsz == '.')
		{
			// skip past '.' separator (width.precision)
			lpsz++;// = _tcsinc(lpsz);

			// get precision and skip it
			if (*lpsz == '*')
			{
				nPrecision = va_arg(argList, int);
				lpsz++;// = _tcsinc(lpsz);
			}
			else
			{
				nPrecision = atoi(lpsz);
				for (; *lpsz != '\0' && isdigit(*lpsz); lpsz ++)
					;
			}
			ASSERT(nPrecision >= 0);
		}

		// should be on type modifier or specifier
		int nModifier = 0;
		if (strncmp(lpsz, "I64", 3) == 0)
		{
			lpsz += 3;
			nModifier = FORCE_INT64;
#if !defined(_X86_) && !defined(_ALPHA_)
			// __int64 is only available on X86 and ALPHA platforms
			ASSERT(FALSE);
#endif
		}
		else
		{
			switch (*lpsz)
			{
			// modifiers that affect size
			case 'h':
				nModifier = FORCE_ANSI;
				lpsz ++;// _tcsinc(lpsz);
				break;
			case 'l':
				nModifier = FORCE_UNICODE;
				lpsz++;// = _tcsinc(lpsz);
				break;

			// modifiers that do not affect size
			case 'F':
			case 'N':
			case 'L':
				lpsz ++;//= _tcsinc(lpsz);
				break;
			}
		}

		// now should be on specifier
		switch (*lpsz | nModifier)
		{
		// single characters
		case 'c':
		case 'C':
			nItemLen = 2;
			va_arg(argList, TCHAR_ARG);
			break;
		case 'c'|FORCE_ANSI:
		case 'C'|FORCE_ANSI:
			nItemLen = 2;
			va_arg(argList, CHAR_ARG);
			break;
		case 'c'|FORCE_UNICODE:
		case 'C'|FORCE_UNICODE:
			nItemLen = 2;
			va_arg(argList, WCHAR_ARG);
			break;

		// strings
		case 's':
			{
				LPCTSTR pstrNextArg = va_arg(argList, LPCTSTR);
				if (pstrNextArg == NULL)
				   nItemLen = 6;  // "(null)"
				else
				{
				   nItemLen = strlen(pstrNextArg);
				   nItemLen = max(1, nItemLen);
				}
			}
			break;

		case 'S':
			{
				LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
				if (pstrNextArg == NULL)
				   nItemLen = 6; // "(null)"
				else
				{
				   nItemLen = strlen(pstrNextArg);
				   nItemLen = max(1, nItemLen);
				}
			}
			break;

		case 's'|FORCE_ANSI:
		case 'S'|FORCE_ANSI:
			{
				LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
				if (pstrNextArg == NULL)
				   nItemLen = 6; // "(null)"
				else
				{
				   nItemLen = strlen(pstrNextArg);
				   nItemLen = max(1, nItemLen);
				}
			}
			break;

		case 's'|FORCE_UNICODE:
		case 'S'|FORCE_UNICODE:
			{
				LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
				if (pstrNextArg == NULL)
				   nItemLen = 6; // "(null)"
				else
				{
				   nItemLen = strlen(pstrNextArg);
				   nItemLen = max(1, nItemLen);
				}
			}
			break;
		}

		// adjust nItemLen for strings
		if (nItemLen != 0)
		{
			if (nPrecision != 0)
				nItemLen = min(nItemLen, nPrecision);
			nItemLen = max(nItemLen, nWidth);
		}
		else
		{
			switch (*lpsz)
			{
			// integers
			case 'd':
			case 'i':
			case 'u':
			case 'x':
			case 'X':
			case 'o':
				if (nModifier & FORCE_INT64)
					va_arg(argList, __int64);
				else
					va_arg(argList, int);
				nItemLen = 32;
				nItemLen = max(nItemLen, nWidth+nPrecision);
				break;

			case 'e':
			case 'g':
			case 'G':
				va_arg(argList, DOUBLE_ARG);
				nItemLen = 128;
				nItemLen = max(nItemLen, nWidth+nPrecision);
				break;

			case 'f':
				{
					double f;
					LPTSTR pszTemp;

					// 312 == strlen("-1+(309 zeroes).")
					// 309 zeroes == max precision of a double
					// 6 == adjustment in case precision is not specified,
					//   which means that the precision defaults to 6
					pszTemp = (LPTSTR)alloca(max(nWidth, 312+nPrecision+6));

					f = va_arg(argList, double);
					sprintf( pszTemp,  "%*.*f" , nWidth, nPrecision+6, f );
					nItemLen = strlen(pszTemp);
				}
				break;

			case 'p':
				va_arg(argList, void*);
				nItemLen = 32;
				nItemLen = max(nItemLen, nWidth+nPrecision);
				break;

			// no output
			case 'n':
				va_arg(argList, int*);
				break;

			default:
				ASSERT(FALSE);  // unknown formatting option
			}
		}

		// adjust nMaxLen for output nItemLen
		nMaxLen += nItemLen;
	}
	return nMaxLen;
}
void TString::FormatV(LPCTSTR lpszFormat, va_list argList)
{
//	ASSERT(AfxIsValidString(lpszFormat));
#ifdef __GNUG__
	va_list argListSave;
	va_copy(argListSave,argList);
#else
	va_list argListSave = argList;
#endif
	// make a guess at the maximum length of the resulting string
	int nMaxLen = 0;
	nMaxLen = vscprintf(lpszFormat,argList);
	GetBuffer(nMaxLen);
	//VERIFY( vsprintf(m_pData, lpszFormat, argListSave) <= m_nMaxSize);
	vsprintf(m_pData, lpszFormat, argListSave);
	ReleaseBuffer();

	va_end(argListSave);
}
void TString::AppendFormatV(const char * lpszFormat, va_list argList)
{
//	ASSERT(AfxIsValidString(lpszFormat));
#ifdef __GNUG__
	va_list argListSave;
	va_copy(argListSave,argList);
#else
	va_list argListSave = argList;
#endif
	
	int nMaxLen = vscprintf(lpszFormat,argList);
	int nCurrentLength = GetLength();
	GetBuffer(nMaxLen+nCurrentLength);
	//VERIFY( vsprintf(m_pData+nCurrentLength, lpszFormat, argListSave) <= m_nMaxSize);
	vsprintf(m_pData+nCurrentLength, lpszFormat, argListSave);
	ReleaseBuffer();

	va_end(argListSave);
}
// formatting (using wsprintf style formatting)
void   TString::Format(LPCTSTR lpszFormat, ...)
{
//	ASSERT(AfxIsValidString(lpszFormat));

	va_list argList;
	va_start(argList, lpszFormat);
	FormatV(lpszFormat, argList);
	va_end(argList);
}
void   TString::AppendFormat(const char * lpszFormat, ...)
{
//	ASSERT(AfxIsValidString(lpszFormat));

	va_list argList;
	va_start(argList, lpszFormat);
	AppendFormatV(lpszFormat, argList);
	va_end(argList);
}